/*
 * Copyright (c) 2023 Cshoo Org. All Rights Reserved.
 */

package org.cshoo.tattoo.authorize.domain.service;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.IteratorUtils;
import org.apache.commons.lang.StringUtils;
import org.cshoo.tattoo.authorize.domain.constant.AccessScope;
import org.cshoo.tattoo.authorize.domain.po.Authority;
import org.cshoo.tattoo.authorize.domain.po.Microservice;
import org.cshoo.tattoo.authorize.domain.repository.IAuthorityRepository;
import org.cshoo.tattoo.authorize.domain.repository.IRoleAuthorityRelationRepository;
import org.springframework.stereotype.Service;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import javax.transaction.Transactional;
import java.util.List;
import java.util.Optional;

/**
 * @author 杨翼
 * @since 1.0
 */
@Service
@Slf4j
public class AuthorityService {

    /**
     * 收集权限点的URL，每个权限点相当于一个服务接口
     */
    public static final String GATHER_AUTHORITIES_URL = "/authority/loadAuthorities";

    @Resource
    private RestTemplate restTemplate;

    @Resource
    private MicroserviceService microserviceService;

    @Resource
    private IAuthorityRepository authorityRepository;

    @Resource
    private IRoleAuthorityRelationRepository authorityRelationRepository;

    /**
     * 创建权限点
     *
     * @param authority 权限点
     */
    public void addAuthority(Authority authority) {
        authorityRepository.save(authority);
    }

    /**
     * 删除权限点
     * 删除权限点，需要删除关联的已经授权的关系
     *
     * @param id id
     */
    @Transactional
    public void deleteAuthority(long id) {
        authorityRelationRepository.deleteByAuthorityId(id);
        authorityRepository.deleteById(id);
    }

    /**
     * 查询所有权限点
     */
    @SuppressWarnings("unchecked")
    public List<Authority> findAllAuthority() {
        Iterable<Authority> authorityIterable = authorityRepository.findAll();
        return IteratorUtils.toList(authorityIterable.iterator());
    }

    /**
     * 根据微服务查询权限点，即查询某个微服务下的全部权限点
     *
     * @param microserviceId 微服务id
     * @return 权限点列表
     */
    public List<Authority> findByMicroserviceId(Long microserviceId) {
        return authorityRepository.findByMicroserviceId(microserviceId);
    }

    /**
     * 根据角色id查询权限点
     *
     * @param roleId 角色id
     * @return 权限点
     */
    public List<Authority> findByRoleId(Long roleId) {
        return authorityRepository.findByRoleId(roleId);
    }

    /**
     * 刷新权限点
     * 将传入的微服务的权限点载入到数据库中
     *
     * @param microserviceId 微服务id
     */
    @Transactional
    public void refreshAuthorities(Long microserviceId) {
        Microservice microservice = microserviceService.findMicroservice(microserviceId);
        String microserviceName = microservice.getName();
        log.info("microserviceName:{}", microserviceName);
        String contextPath = microservice.getContextPath();
        String url = "http://" + microserviceName + "/" + contextPath + GATHER_AUTHORITIES_URL;
        List<Authority> currentAuthorities = this.findByMicroserviceId(microserviceId);
        Authority[] authorities = restTemplate.getForObject(url, Authority[].class);
        assert authorities != null;
        log.info("authorities length:{}", authorities.length);
        AntPathMatcher antPathMatcher = new AntPathMatcher();
        out:
        for (Authority currentAuthority : currentAuthorities) {
            String currentUri = currentAuthority.getUri();
            String currentMethod = currentAuthority.getMethod();
            for (Authority authority : authorities) {
                String uri = authority.getUri();
                String method = authority.getMethod();
                if ((StringUtils.equals(uri, currentUri) || antPathMatcher.match(uri, currentUri)) && StringUtils.equalsIgnoreCase(method, currentMethod)) {
                    continue out;
                }
            }
            log.info("delete authority, name: {}, uri:{}, method:{}", currentAuthority.getName(), currentUri, currentMethod);
            this.deleteAuthority(currentAuthority.getId());
        }

        out:
        for (Authority authority : authorities) {
            String uri = authority.getUri();
            String method = authority.getMethod();
            log.debug("add block, uri:{},method:{}", uri, method);
            for (Authority currentAuthority : currentAuthorities) {
                String currentUri = currentAuthority.getUri();
                String currentMethod = currentAuthority.getMethod();
                log.debug("add block,currentUri:{},currentMethod:{}", currentUri, currentMethod);
                if ((StringUtils.equals(uri, currentUri) || antPathMatcher.match(currentUri, uri)) && StringUtils.equalsIgnoreCase(method, currentMethod)) {
                    currentAuthority.setUri(uri);
                    currentAuthority.setMethod(method);
                    currentAuthority.setName(authority.getName());
                    authorityRepository.save(currentAuthority);
                    continue out;
                }
            }
            authority.setMicroserviceId(microserviceId);
            authority.setAccessScope(AccessScope.GENERAL.getValue());
            authorityRepository.save(authority);
        }
    }

    /**
     * 取定访问范围
     *
     * @param id          id
     * @param accessScope access范围
     */
    public void fixAccessScope(Long id, Integer accessScope) {
        log.info("path:{},accessScope:{}", id, accessScope);
        Optional<Authority> authorityOptional = authorityRepository.findById(id);
        if (authorityOptional.isPresent()) {
            Authority authority = authorityOptional.get();
            authority.setAccessScope(accessScope);
            authorityRepository.save(authority);
        }
    }

}